/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.component.helper;

import java.util.Iterator;

import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.uml2.uml.AggregationKind;
import org.eclipse.uml2.uml.Component;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.EncapsulatedClassifier;
import org.eclipse.uml2.uml.Port;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.UMLFactory;

import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagraminterchange2.di2.Diagram;
import com.cea.papyrus.diagraminterchange2.di2.DiagramElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;
/**
 * Factory that can create and delete a {@link org.eclipse.uml2.uml.Port} in the context of
 * a composite diagram.
 */
public class NodePortHelper extends com.cea.papyrus.diagram.composite.helper.NodePortHelper {

	///////////////////////////////////////////////////////////////////////////
	// GRAPHICAL MANAGEMENT 
	///////////////////////////////////////////////////////////////////////////

	/**
	 * The default container. 
	 */
	private GraphElement defaultContainer;


	/**
	 * 
	 * 
	 * @param parent 
	 * 
	 * @return 
	 */
	@Override
	public boolean canCreateGraphElement(GraphElement parent) {
		if(parent instanceof Diagram) {
			return false; //this a diagram
		} else if (parent.getSemanticModel()!=null){
			Element parentElt= ((Uml1SemanticModelBridge)parent.getSemanticModel()).getElement();
			if(parentElt!=null){
				if (parentElt instanceof org.eclipse.uml2.uml.Component ){
					return true;
				}
				if (parentElt instanceof org.eclipse.uml2.uml.Package ){
					return true;
				}
				if (parentElt instanceof org.eclipse.uml2.uml.Class ){
					return true;
				}
				else if (parentElt instanceof org.eclipse.uml2.uml.Property ){
					if(((Property)parentElt).getType() instanceof org.eclipse.uml2.uml.Class){
						return true;
					}
				}
			}
		}
		return false;
	}

//	@Override
//	public GraphNode createGraphElement(Element umlChild, GraphElement parent, Point location, Dimension size){
//		
//		// Set parent container of nodeElement
//
//			defaultContainer = parent.getContainer();
//		
//		// Create the graph element corresponding to the Primitive Type
//		// graph element must be added to the parent graph element (specific to port)
//		GraphNode node = super.createGraphElement(umlChild, parent.getContainer(), location, size);
////		GraphNode node = super.createGraphElement(umlChild, null, location, size);
//		// Set position of graphNode
//		// all calculation are made in absolute positions
//		Point newpos= new Point(0,0);
//		newpos=LookForElement.calculateAbsolutePosition((GraphNode)parent, location);
//		
//		//to say that the port belongs to entity parent
//		node.setSemanticParent(parent);
//		
//		// set default size, if null
//		node.setSize(new Dimension(20, 20));
//
//		// set correct position: calculate position for the port
//		node.setPosition(inRunner((GraphNode)parent, newpos, node.getSize()));
//		Point origin=LookForElement.calculateAbsolutePosition((GraphNode)parent.getContainer(),new Point(0,0));
//		node.setPosition(node.getPosition().getTranslated(origin.getNegated().getCopy()));
//				
//	//parent.getContainer().getContained().add(node);
//		
//		//create label
//		NodeHelper nh= new NodeHelper();
//		GraphElement ge= nh.createGraphElement(null, null, new Point(node.getPosition().x+15, node.getPosition().y+15), null);
//		ge.setSemanticParent(node);
//
//		parent.getContainer().getContained().add(ge);
////		ge.setContainer(parent.getContainer());
////		parent.getContained().add(graphNode);
//		return node;
//	}

	///////////////////////////////////////////////////////////////////////////
	// UML MANAGEMENT 
	///////////////////////////////////////////////////////////////////////////

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.NodeFactory#createUMLElement(java.lang.String, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param umlParent 
	 * @param name 
	 * 
	 * @return 
	 */
	@Override
	public  org.eclipse.uml2.uml.Element createUMLElement(String name,  org.eclipse.uml2.uml.Element umlParent){
		Debug.debug(this,"Call createPort()", channel);
		EncapsulatedClassifier portContainer=null;
//		if (umlParent instanceof Property){
//			portContainer= (EncapsulatedClassifier)((Property) umlParent).getType();
//		}
		if (umlParent instanceof Component){
			portContainer=(Component) umlParent;
		}
		else{
			//class
			portContainer=(EncapsulatedClassifier)umlParent;
		}
		org.eclipse.uml2.uml.Port aport=UMLFactory.eINSTANCE.createPort();
		aport.setName(name);
		aport.setAggregation(AggregationKind.COMPOSITE_LITERAL);
		portContainer.getOwnedPorts().add(aport);
		return aport;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.NodeFactory#canExecuteCreateElement(org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param umlParent 
	 * 
	 * @return 
	 */
//	@Override
	public boolean canCreateUMLElement(Element umlParent) {
//		if(umlParent instanceof org.eclipse.uml2.uml.EncapsulatedClassifier||umlParent instanceof Property || umlParent instanceof Component) {
//			return true;
//		}
		if(umlParent instanceof Component || umlParent instanceof org.eclipse.uml2.uml.EncapsulatedClassifier) {
			return true;
		}
		
		return false;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.NodeFactory#addElement(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param element 
	 * @param umlParent 
	 * 
	 * @return 
	 */
	@Override
	public boolean addUMLElement(Element element, Element umlParent) {
		if(umlParent instanceof Component) {
			if(((Component)umlParent).getOwnedPorts().contains(element)){
				return true;
			}
			return ((Component)umlParent).getOwnedPorts().add((Port)element);
		}
		if(umlParent instanceof org.eclipse.uml2.uml.EncapsulatedClassifier) {
			if(((EncapsulatedClassifier)umlParent).getOwnedPorts().contains(element)){
				return true;
			}
			return ((EncapsulatedClassifier)umlParent).getOwnedPorts().add((Port)element);
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.NodeFactory#removeElement(org.eclipse.uml2.uml.Element, org.eclipse.uml2.uml.Element)
	 */
	/**
	 * 
	 * 
	 * @param element 
	 * @param umlParent 
	 * 
	 * @return 
	 */
	@Override
	public boolean removeUMLElement(Element element, Element umlParent) {
		if(umlParent instanceof Component ) {
			return ((Component)umlParent).getOwnedPorts().remove(element);
		}
		if(umlParent instanceof org.eclipse.uml2.uml.EncapsulatedClassifier) {
			return ((EncapsulatedClassifier)umlParent).getOwnedPorts().remove(element);
		}
		return false;
	}
	/**
	 * 
	 * 
	 * @param child 
	 * @param parent 
	 * 
	 * @return 
	 */
	public boolean undoCreateGraphElement(GraphElement child, GraphElement parent){
		child.setSemanticParent(null);
		child.eAdapters().clear();
		Iterator<DiagramElement> itchild=child.getSemanticChild().iterator();
		for(int i=0; i<child.getSemanticChild().size();){
			GraphElement subchild=(GraphElement)child.getSemanticChild().get(i);
			subchild.eAdapters().clear();
			subchild.setSemanticParent(null);
			subchild.getContainer().getContained().remove(subchild);
		}
		 child.getContainer().getContained().remove(child);
		 return true;
	}
	

	/**
 * this method calculate the good position of a port by taking account of his parent.
 * 
 * @param portDimension the dimension of the port
 * @param parentGP graphNode the virtual graphNode that contains the port
 * @param point the absolute position
 * 
 * @return the new position of the port in absolute coordinate
 */
	@Override
	public Point inRunner(GraphNode parentGP,Point point, Dimension portDimension){
		int x= point.x;
		int y= point.y;
		Point selected =point.getCopy();
		//calculate absolute position of GP
		Point positionGP=LookForElement.calculateAbsolutePosition(parentGP,new Point(0,0));


		// position is constraint in the parent rectangle
		if (x<positionGP.x-portDimension.width/2){
			x=positionGP.x-portDimension.width/2;
		}
		if (x>positionGP.x+parentGP.getSize().width-portDimension.width/2){
			x=positionGP.x+parentGP.getSize().width-portDimension.width/2;
		}

		if (y<positionGP.y-portDimension.height/2){
			y=positionGP.y-portDimension.height/2;
		}
		if (y>positionGP.y+parentGP.getSize().height-portDimension.height/2){
			y=positionGP.y+parentGP.getSize().height-portDimension.height/2;
		}

		//calculate all 4 possible positions.		
		Point pointWEST= new Point(positionGP.x-portDimension.width/2, y);
		Point pointNORTH= new Point(x,positionGP.y-portDimension.height/2);
		Point pointEAST= new Point(positionGP.x+parentGP.getSize().width-portDimension.width/2, y);
		Point pointSOUTH= new Point(x,positionGP.y+parentGP.getSize().height-portDimension.height/2);

		// calculate all distance
		double distanceWEST=pointWEST.getDistance(point);
		double distanceNORTH=pointNORTH.getDistance(point);
		double distanceEAST=pointEAST.getDistance(point);
		double distanceSOUTH=pointSOUTH.getDistance(point);
		Debug.debug(this,"pointWEST: "+pointWEST+" "+distanceWEST,channel);
		Debug.debug(this,"pointNORTH: "+pointNORTH+" "+distanceNORTH,channel);
		Debug.debug(this,"pointEAST: "+pointEAST+" "+distanceEAST,channel);
		Debug.debug(this,"pointSOUTH: "+pointSOUTH+" "+distanceSOUTH,channel);

		//choose the point the minimum distance
		selected=pointWEST;
		double selecteddistance=distanceWEST;
		if (selecteddistance>distanceNORTH){
			selected=pointNORTH;
			selecteddistance=distanceNORTH;}

		if (selecteddistance>distanceEAST){
			selected=pointEAST;
			selecteddistance=distanceEAST;}

		if (selecteddistance>distanceSOUTH){
			selected=pointSOUTH;
			selecteddistance=distanceSOUTH;}
		Debug.debug(this,"miminum distance: "+selected+" "+selecteddistance,channel);
		
		
		return selected;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.factories.NodeFactory#getBaseString()
	 */
	/**
	 * 
	 * 
	 * @return 
	 */
	@Override
	public String getBaseString() {
		return "Port_";
	}

}